package cn.sdack.go.users.service;

import cn.sdack.go.common.annotation.AfterAuthentication;
import cn.sdack.go.common.annotation.BeforeAuthentication;
import cn.sdack.go.common.dtos.users.UserDTO;
import cn.sdack.go.common.entities.users.UserEntity;
import cn.sdack.go.common.querys.users.UserQuery;
import cn.sdack.go.users.dao.UserDao;
import com.alibaba.fastjson2.JSONObject;
import jakarta.persistence.criteria.Predicate;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.*;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;


/**
 * @author sdack
 * @date 2024/1/4
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao usersDao;

    @Autowired
    private UserRoleService userRoleService;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String authUrl;

    public final String REGEX_EMAIL = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";

    @Override
    public UserEntity userinfo(String account) {
        UserEntity userEntity = usersDao.findByAccount(account);
        if (userEntity != null) {
            userEntity.setPhone("");
            userEntity.setEmail("");
        }
        return userEntity;
    }

    private Specification<UserEntity> getSpecification(UserQuery parma) {
        return (root, query, builder) -> {
            List<Predicate> predicate = new ArrayList<>();

            if (parma.getIds() != null && !parma.getIds().isEmpty()) {
                predicate.add(builder.and(root.get(UserQuery.ID).in(parma.getIds())));
            }
            if (parma.getAccounts() != null && !parma.getAccounts().isEmpty()) {
                predicate.add(builder.and(root.get(UserQuery.ACCOUNT).in(parma.getAccounts())));
            }
            if (parma.getEmail() != null && !"".equals(parma.getEmail())) {
                predicate.add(builder.like(root.get(UserQuery.EMAIL), "%" + parma.getEmail() + "%"));
            }

            if (parma.getPhone() != null && !"".equals(parma.getPhone())) {
                predicate.add(builder.like(root.get(UserQuery.PHONE), "%" + parma.getPhone()));
            }

            if (parma.getNickname() != null && !"".equals(parma.getNickname())) {
                predicate.add(builder.like(root.get(UserQuery.NICKNAME), "%" + parma.getNickname()));
            }

            predicate.add(builder.isNull(root.get("del")));
            Predicate[] pre = new Predicate[predicate.size()];
            return query.where(predicate.toArray(pre)).getRestriction();
        };

    }

    private final RestTemplate restTemplate = new RestTemplate();

    @BeforeAuthentication
    @Override
    public Page<UserEntity> queryAll(UserQuery param, int page, int pageSize) throws Exception {
        return usersDao.findAll(getSpecification(param), PageRequest.of(page <= 0 ? 0 : page - 1, pageSize, Sort.by(Sort.Order.desc("id"))));
    }

    @AfterAuthentication
    @Override
    public UserEntity getById(Long id) throws IllegalAccessException {
        Optional<UserEntity> optional = usersDao.findById(id);
        if (optional.isEmpty()) {
            throw new IllegalAccessException("non-existent");
        }
        return optional.get();
    }

    @BeforeAuthentication
    @Override
    public UserEntity add(UserDTO param) throws IllegalAccessException {
        UserEntity userEntity = new UserEntity();
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        Long pid = 0L;
        Long gid = 0L;
        Long uid = 0L;
        String role = "";
        String tokenValue = authentication.getToken().getTokenValue();
        try {
            uid = Long.parseLong(authentication.getToken().getClaim("uid").toString());
            pid = Long.parseLong(authentication.getToken().getClaim("pid").toString());
            gid = Long.parseLong(authentication.getToken().getClaim("gid").toString());
            role = authentication.getToken().getClaim("role").toString();
        } catch (Exception e) {
            throw new IllegalAccessException("权限检查失败");
        }
        userEntity.setGid(gid);
        if (UserEntity.Identity.SIMPLE.code.equals(role)) {
            userEntity.setPid(pid);
        } else if (UserEntity.Identity.SUPER.code.equals(role)) {
            userEntity.setPid(uid);
        } else if (UserEntity.Identity.ADMIN.code.equals(role)) {
            userEntity.setPid(pid);
        } else if (UserEntity.Identity.GROUP_ADMIN.code.equals(role)) {
            userEntity.setPid(uid);
        } else if (UserEntity.Identity.GROUP.code.equals(role)) {
            userEntity.setPid(pid);
        } else {
            userEntity.setPid(uid);
        }
        MultiValueMap<String,String> form = new LinkedMultiValueMap<>();
        if (StringUtils.hasLength(param.getUsername())) {
            userEntity.setUsername(param.getUsername());
        } else {
            userEntity.setUsername("");
        }
        if (StringUtils.hasLength(param.getNickname())) {
            form.add("nickname", param.getNickname());
            userEntity.setNickname(param.getNickname());
        } else {
            userEntity.setNickname("");
        }
        if (StringUtils.hasLength(param.getEmail())) {
            if (!Pattern.matches(REGEX_EMAIL, param.getEmail())) {
                throw new IllegalAccessException("邮箱格式不正确");
            }
            form.add("email", param.getEmail());
            userEntity.setEmail(param.getEmail());
            UserEntity email = usersDao.findByEmailAndDelIsNull(param.getEmail());
            if (email != null) {
                throw new IllegalAccessException("邮箱已存在");
            }
        } else {
            userEntity.setEmail("");
        }
        if (StringUtils.hasLength(param.getPhone())) {
            form.add("mobile", param.getPhone());
            userEntity.setPhone(param.getPhone());
            UserEntity email = usersDao.findByPhoneAndDelIsNull(param.getPhone());
            if (email != null) {
                throw new IllegalAccessException("手机已存在");
            }
        } else {
            userEntity.setPhone("");
        }
        if (StringUtils.hasLength(param.getAvatar())) {
            form.add("avatar", param.getAvatar());
            userEntity.setAvatar(param.getAvatar());
        }
        if (StringUtils.hasLength(param.getPwd())) {
            if (param.getPwd().length() < 6) {
                throw new IllegalAccessException("密码长度太短");
            }
            form.add("password", param.getPwd());
        }
        if (StringUtils.hasLength(authUrl)) {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            headers.setBearerAuth(tokenValue);
            HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(form, headers);
            ResponseEntity<String> response = restTemplate.postForEntity(authUrl + "/register/account", request, String.class);
            if (response.getStatusCode().is2xxSuccessful()) {
                JSONObject body = JSONObject.parseObject(response.getBody());
                if (body!= null && body.getBooleanValue("succeed")) {
                    JSONObject data = body.getJSONObject("data");
                    if (data!= null && !data.isEmpty()) {
                        String account = data.getString("account");
                        userEntity.setAccount(account);
                    }
                }
            } else {
                throw new IllegalAccessException("创建账户失败");
            }
        }
        userEntity.setIdentity(UserEntity.Identity.SIMPLE.code);
        return usersDao.save(userEntity);
    }


    @BeforeAuthentication
    @Transactional
    @Override
    public UserEntity editUserinfo(UserDTO param) throws IllegalAccessException {
        Optional<UserEntity> optional = usersDao.findById(param.getId());
        if (optional.isEmpty()) {
            throw new IllegalAccessException("non-existent");
        }
        UserEntity userEntity = optional.get();
        if (StringUtils.hasLength(param.getUsername())) {
            userEntity.setUsername(param.getUsername());
        }
        if (StringUtils.hasLength(param.getNickname())) {
            userEntity.setNickname(param.getNickname());
        }
        if (StringUtils.hasLength(param.getEmail())) {
            if (!Pattern.matches(REGEX_EMAIL, param.getEmail())) {
                throw new IllegalAccessException("邮箱格式不正确");
            }
            userEntity.setEmail(param.getEmail());
        }
        if (StringUtils.hasLength(param.getPhone())) {
            userEntity.setPhone(param.getPhone());
        }
        if (StringUtils.hasLength(param.getAvatar())) {
            userEntity.setAvatar(param.getAvatar());
        }
        return usersDao.save(userEntity);
    }

    /**
     * 设置身份
     * @param uid
     * @param identity
     * @throws Exception
     */
    @Override
    public void setIdentity(Long uid, String identity) throws Exception {
        Optional<UserEntity> optional = usersDao.findById(uid);
        if (optional.isEmpty()) {
            throw new IllegalAccessException("non-existent");
        }
        long count = Arrays.stream(UserEntity.Identity.values()).filter(it -> it.code.equals(identity)).count();
        if (count == 0) {
            throw new IllegalAccessException("未知的身份 code");
        }
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        Long pid = 0L;
        Long gid = 0L;
        Long uuid = 0L;
        String role = "";
        try {
            uuid = Long.parseLong(authentication.getToken().getClaim("pid").toString());
            pid = Long.parseLong(authentication.getToken().getClaim("pid").toString());
            gid = Long.parseLong(authentication.getToken().getClaim("gid").toString());
            role = authentication.getToken().getClaim("role").toString();
        } catch (Exception e) {
            throw new IllegalAccessException("权限检查失败");
        }
        UserEntity userEntity = optional.get();
        if (uuid.longValue() == uid) {
            throw new IllegalAccessException("无权更改");
        }
        String newRole = "";
        if (UserEntity.Identity.SUPER.code.equals(role)) {
            if (UserEntity.Identity.SUPER.code.equals(userEntity.getIdentity())) {
                throw new IllegalAccessException("无权修改");
            }
            newRole = UserEntity.Identity.ADMIN.code;
        } else if (UserEntity.Identity.ADMIN.code.equals(role)) {
            throw new IllegalAccessException("无权修改");
        } else if (UserEntity.Identity.GROUP_ADMIN.code.equals(role)) {
            if (gid != userEntity.getGid().longValue()) {
                throw new IllegalAccessException("用户组织鉴定失败");
            }
            if (UserEntity.Identity.GROUP_ADMIN.code.equals(userEntity.getIdentity()) || UserEntity.Identity.ADMIN.code.equals(userEntity.getIdentity()) || UserEntity.Identity.SUPER.code.equals(userEntity.getIdentity())) {
                throw new IllegalAccessException("无权修改");
            }
            if (UserEntity.Identity.SIMPLE.code.equals(identity)) {
                newRole = UserEntity.Identity.SIMPLE.code;
            } else {
                newRole = UserEntity.Identity.GROUP.code;
            }
        } else if (UserEntity.Identity.GROUP.code.equals(role)) {
            if (gid != userEntity.getGid().longValue()) {
                throw new IllegalAccessException("用户组织鉴定失败");
            }
            if (UserEntity.Identity.GROUP_ADMIN.code.equals(userEntity.getIdentity()) || UserEntity.Identity.ADMIN.code.equals(userEntity.getIdentity()) || UserEntity.Identity.SUPER.code.equals(userEntity.getIdentity())) {
                throw new IllegalAccessException("无权修改");
            }
            if (UserEntity.Identity.SIMPLE.code.equals(identity)) {
                newRole = UserEntity.Identity.SIMPLE.code;
            } else {
                newRole = UserEntity.Identity.GROUP.code;
            }
        } else {
            throw new IllegalAccessException("未知的身份 code");
        }

        userEntity.setIdentity(newRole);
        usersDao.save(userEntity);
    }

    /**
     * 设置用户状态
     * @param uid
     * @throws Exception
     */
    @Transactional
    @Override
    public void setState(Long uid , int state) throws Exception {
        boolean b = state != 0;
        UserEntity entity = getById(uid);
        if (entity.getStats() == b) {
            return;
        }
        entity.setStats(b);
        int l = entity.getStats() ? 0 : 1;
        JwtAuthenticationToken authentication = (JwtAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        String tokenValue = authentication.getToken().getTokenValue();
        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(tokenValue);
        HttpEntity<MultiValueMap<String,String>> request = new HttpEntity<>(null, headers);
        ResponseEntity<String> response = restTemplate.exchange(authUrl + "/account/lock?a=" + entity.getAccount() + "&l=" + l, HttpMethod.GET,request, String.class);
        if (response.getStatusCode().is2xxSuccessful()) {
            JSONObject result = JSONObject.parseObject(response.getBody());
            assert result != null;
            if (!result.getBooleanValue("succeed")) {
                throw new IllegalAccessException("远程操作失败");
            }
            usersDao.save(entity);
        } else {
            throw new IllegalAccessException("远程响应失败");
        }
    }


}
